// 用户调用入口

package cf

import (
	"gitee.com/ctyunfaas/cf-runtime-go-sdk/config"
	"gitee.com/ctyunfaas/cf-runtime-go-sdk/util"
	"log"
	"net/http"
	"os"
	"strconv"
)

var (
	logFatalf = log.Fatalf
	logPrintf = log.Printf
	sem       chan struct{} // 用作信号量的 channel，用于控制并发数

	enableInvokePanicLog = true
	funcType             = config.EventFunction
)

// event handler 入口
//
// 支持的函数签名:
//
//	func ()
//	func () error
//	func (TIn) error
//	func () (TOut, error)
//	func (TIn) (TOut, error)
//	func (context.Context) error
//	func (context.Context, TIn) error
//	func (context.Context) (TOut, error)
//	func (context.Context, TIn) (TOut, error)
//
// 其中 TIn, TOut 需要和 encoding/json 标准库兼容
//
// json.Unmarshal 对传入的 TIn 进行反序列化，以及使用 json.Marshal 对返回的 TOut 进行序列化
func Start(handler interface{}) {
	log.SetFlags(log.LstdFlags | log.Lmicroseconds) // 配置默认日志格式
	log.SetOutput(os.Stdout)                        // 配置 log 输出到 stdout, 避免日志输出次序错乱

	logPrintf("golang runtime started...\n")

	// 配置单实例最大并发量
	concurrentLimit := util.GetEnvOrDefault(config.FC_CONCURRENT_LIMIT, config.DefaultConcurrent)
	limit, err := strconv.Atoi(concurrentLimit)
	if err != nil {
		logFatalf("%v", err)
	}
	if limit > 1 { // 根据 Knative 规范，limit < 1 时，不限制并发量
		sem = make(chan struct{}, limit) // channel 作为信号量控制单实例最大并发
	}

	uf := newFunction(handler, funcType)           // 创建存储用户函数相关信息的对象
	uf.registryLifeCycleHandler(lifeCycleHandlers) // 注册用户函数提供的生命周期回调

	http.HandleFunc("/", uf.requestHandler)
	// 启动服务，接受请求
	port := util.GetEnvOrDefault(config.FC_SERVER_PORT, config.DefaultPort)
	localAddr := ":" + port
	if err := http.ListenAndServe(localAddr, nil); err != nil {
		logFatalf("failed to start runtime server:%v", err)
	}
}

// http handler 入口
//
// http handler 仅支持如下函数签名:
//
//	func(context.Context, http.ResponseWriter, *http.Request) error
func StartHttp(handler interface{}) {
	funcType = config.HttpFunction
	Start(handler)
}
